using System;
using System.Runtime;
using System.Security.Permissions;
using System.Xml;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Security;

using WMOSSExperts.Helper;

namespace WMOSSExperts.Features
{
    [CLSCompliant(false)]
    public class BaseFeatureReceiver : SPFeatureReceiver
    {

#region "Constants definition"

        private const String _kUpdateWebConfigFileKey = "UpdateWebConfig";

        /// <summary>
        /// attribute name in definition of the updating web.config file 
        /// </summary>
        private const String _kAttributeName = "name";
        private const String _kAttributePath = "path";
        private const String _kAttributeValue = "value";

#endregion

        #region "Members / properties"

        /// <summary>
        /// Properties arguments from the feature operation
        /// </summary>
        SPFeatureReceiverProperties _properties;
        SPFeatureReceiverProperties Properties
        {
            get { return _properties; }
            set
            {
                _properties = value;
                this.InitializeFeatureScope();
            }

        }

        SPWebService _service;
        public SPWebService Service
        {
            get { return _service; }
        }

        /// <summary>
        /// Get the web application
        /// </summary>
        SPWebApplication _webApplication;
        public SPWebApplication WebApplication
        {
            get{return _webApplication;}
        }

        SPSite _site;
        public SPSite Site
        {
            get{return _site;}
        }

        SPWeb _web;
        public SPWeb Web
        {
            get { return _web; }
        }

        /// <summary>
        /// Get the feature id
        /// </summary>
        public Guid FeatureId
        {
            get
            {
                return this.Properties.Definition.Id;
            }
        }

        #endregion


        #region "Private / public methods to help the features management of this classe"
        /// <summary>
        /// Initialize feature receiver according to the activation scope
        /// </summary>
        private void InitializeFeatureScope()
        {
            //Check if properties are initialized
            this.CheckFeatureProperties();

            switch (this.Properties.Definition.Scope)
            {
                case SPFeatureScope.WebApplication:
                    _webApplication = (this.Properties.Feature.Parent as SPWebApplication);
                    break;
                case SPFeatureScope.Site:
                    _site = (this.Properties.Feature.Parent as SPSite);
                    _webApplication = _site.WebApplication;
                    break;
                case SPFeatureScope.Web:
                    _web = (this.Properties.Feature.Parent as SPWeb);
                    _site = _web.Site;
                    _webApplication = _site.WebApplication;
                    break;
                case SPFeatureScope.Farm:
                    _service = (this.Properties.Feature.Parent as SPWebService);
                    break;
                default:
                    break;
            }

        }


        /// <summary>
        /// Check that the properties are well initialized
        /// </summary>
        private void CheckFeatureProperties()
        {
            if (this.Properties == null)
            {
                throw new SPException("Properties argument can not be null on the feature operation");
            }
        }


        /// <summary>
        /// Get a property value in the feature.xml file <Properties><Property key></Property></Properties>
        /// </summary>
        /// <param name="KeyName"></param>
        /// <returns></returns>
        protected String GetPropertyValue(String KeyName)
        {
            //Check if properties are well definied
            if (this.Properties == null) return String.Empty;

            //Read the property key in the web config file
            SPFeatureProperty featureProperty = this.Properties.Definition.Properties[KeyName] as SPFeatureProperty;
            if (featureProperty == null) return String.Empty;

            return featureProperty.Value;

        }


        #endregion

        #region "Define methods which can be overriden by other developper"

        public virtual void OnFeatureActivated(SPFeatureReceiverProperties properties) { }

        public virtual void OnFeatureDeactivating(SPFeatureReceiverProperties properties) { }
        #endregion


        [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
        public override sealed void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            this.Properties = properties;

            //Check if web config modifications must be done
            String updateWebConfigFile = this.GetPropertyValue(_kUpdateWebConfigFileKey);
            if (!String.IsNullOrEmpty(updateWebConfigFile)) this.UpdateWebConfig(updateWebConfigFile);

            this.OnFeatureActivated(properties);
        }


        [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
        public override sealed void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            this.Properties = properties;
            //Remove update in the web.config file
            this.RemoveAllUpdateWebConfig();

            this.OnFeatureDeactivating(properties);

        }

        [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
        public override void FeatureInstalled(SPFeatureReceiverProperties properties)
        {
            //this.Properties = properties;
        }

        [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
        public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
        {
            //this.Properties = properties;
        }


        #region "Web.config update"


        /// <summary>
        /// Remove all upates from the web.config which have been added by the feature
        /// </summary>
        protected virtual void RemoveAllUpdateWebConfig()
        {
            if (this.WebApplication != null)
            {
                webConfigEditor oEditor = new webConfigEditor(this.WebApplication);
                oEditor.RemoveAll(this.FeatureId.ToString());
            }
        }

        /// <summary>
        /// Update the Web.config file when the feature is activated
        /// </summary>
        /// <param name="RelativePath"></param>
        protected virtual void UpdateWebConfig(String RelativePath)
        {
            if (this.WebApplication == null) return;

            //Open the XMl file which contains the web.config modifications
            XmlDocument oFile = this.OpenXMLFile(RelativePath);
            if (oFile == null) return;


            //Search adding actions and removing actions
            XmlNode addingNode = oFile.SelectSingleNode("/Actions[@type='Add']");
            XmlNode removingNode = oFile.SelectSingleNode("/Actions[@type='Remove']");

            //If there is nothing to do, return else create the webConfigEditor Object
            if (addingNode == null & removingNode == null) return;

            webConfigEditor oEditor = new webConfigEditor(this.WebApplication);
            oEditor.BeginTrans();
            
            try
            {
                if (addingNode != null) this.AddUpdateToWebConfig(addingNode, oEditor);

                //Search removing actions
                if (removingNode != null) this.AddUpdateToWebConfig(removingNode, oEditor);

                oEditor.Commit();
            }
            catch(Exception ex)
            {
                //TODO manage exception
                oEditor.RollBack();
            }


        }

        /// <summary>
        /// Add update to the web.config file
        /// </summary>
        /// <param name="AddingNode"></param>
        /// <param name="Editor"></param>
        private void AddUpdateToWebConfig(XmlNode AddingNode, webConfigEditor Editor)
        {
            //Check entry arguments
            if (AddingNode == null) return;

            //Browse if there are attributes modification
            XmlNodeList addingAttributes = AddingNode.SelectNodes("Attributes/Attribute");
            this.AddAtributeModification(addingAttributes, Editor);

            //Browse if there are attributes modification
            XmlNodeList addingChildNodes = AddingNode.SelectNodes("ChildNodes/ChildNode");
            this.AddAtributeModification(addingAttributes, Editor);


        }

        /// <summary>
        /// Remove update to the web.config file
        /// </summary>
        /// <param name="AddingNode"></param>
        /// <param name="Editor"></param>
        private void RemoveUpdateToWebConfig(XmlNode RemovingNode, webConfigEditor Editor)
        {
            //Check entry arguments
            if (RemovingNode == null) return;

            //Browse if there are attributes modification
            XmlNodeList removingAttributes = RemovingNode.SelectNodes("Attributes/Attribute");
            this.RemoveAtributeModification(removingAttributes, Editor);

        }


        /// <summary>
        /// Check that the attribute modification node is well configured
        /// </summary>
        /// <param name="Node"></param>
        /// <returns></returns>
        private Boolean CheckWebConfigurationModification(XmlNode Node, Boolean ForRemoving)
        {
            XmlAttribute att = Node.Attributes[_kAttributeName];
            if (att == null || String.IsNullOrEmpty(att.Value)) return false;

            att = Node.Attributes[_kAttributePath];
            if (att == null || String.IsNullOrEmpty(att.Value)) return false;

            if (!ForRemoving)
            {
                att = Node.Attributes[_kAttributeValue];
                if (att == null) return false;
            }

            return true;
        }

        private void AddChildNodeModification(XmlNodeList AddingChildNodes, webConfigEditor Editor)
        {
            //Check entry arguments
            if (AddingChildNodes == null) return;
        }

        /// <summary>
        /// Add the attribute modifications
        /// </summary>
        /// <param name="AttributeModifications"></param>
        private void AddAtributeModification(XmlNodeList AddingAttributes, webConfigEditor Editor)
        {
            //Check entry arguments
            if (AddingAttributes == null) return;

            //Browse attributes modifications
            foreach (System.Xml.XmlNode oNode in AddingAttributes)
            {
                if (this.CheckWebConfigurationModification(oNode, false))
                {
                    String Name = oNode.Attributes[_kAttributeName].Value;
                    String Path = oNode.Attributes[_kAttributePath].Value;
                    String Value = oNode.Attributes[_kAttributeValue].Value;

                    Editor.AddAttributeModification(this.FeatureId.ToString(), Name, Path, Value);
                }
                else
                {
                    //TODO manage when attribute modification
                }
            }
        }

        /// <summary>
        /// Remove the attribute modifications
        /// </summary>
        /// <param name="AttributeModifications"></param>
        private void RemoveAtributeModification(XmlNodeList RemovingAttributes, webConfigEditor Editor)
        {
            //Check entry arguments
            if (RemovingAttributes == null) return;

            //Browse attributes modifications
            foreach (System.Xml.XmlNode oNode in RemovingAttributes)
            {
                if (this.CheckWebConfigurationModification(oNode, true))
                {
                    String Name = oNode.Attributes[_kAttributeName].Value;
                    String Path = oNode.Attributes[_kAttributePath].Value;

                    Editor.RemoveModification(this.FeatureId.ToString(), Name, Path);
                }
                else
                {
                    //TODO manage when attribute modification
                }
            }
        }


        #endregion



        /// <summary>
        /// Open a xml file which contains Configuration to be activated by the object model
        /// </summary>
        /// <param name="RelativePath">Path of the file since the feature root directory</param>
        public System.Xml.XmlDocument OpenXMLFile(String RelativePath)
        {
            //Define the path to access to the file
            String sPath = this.Properties.Definition.RootDirectory + @"\" + RelativePath;

            if (System.IO.File.Exists(sPath))
            {
                System.Xml.XmlDocument ConfigFile = new System.Xml.XmlDocument();
                ConfigFile.Load(sPath);

                return ConfigFile;
            }
            else
                return null;
        }


    }
}
